#!/usr/bin/env python

import glob, re
from standard_script_setup import *
from CIME.utils import expect, run_bld_cmd_ensure_logging, safe_copy
from CIME.build import get_standard_makefile_args
from CIME.case import Case

logger = logging.getLogger(__name__)

def parse_command_line(args, description):
###############################################################################
    parser = argparse.ArgumentParser(
        usage="""\n{0} [--debug]
OR
{0} --verbose
OR
{0} --help

\033[1mEXAMPLES:\033[0m
    \033[1;32m# Run \033[0m
    > {0}
""" .format (os.path.basename(args[0])),

description=description,

formatter_class=argparse.ArgumentDefaultsHelpFormatter
)

    CIME.utils.setup_standard_logging_options(parser)

    parser.add_argument("buildroot",
                        help="build path root")

    parser.add_argument("installpath",
                        help="install path ")

    parser.add_argument("caseroot", nargs="?", default=os.getcwd(),
                        help="Case directory to build")

    args = CIME.utils.parse_args_and_handle_standard_logging_options(args, parser)

    return args.buildroot, args.installpath, args.caseroot

###############################################################################
def buildlib(bldroot, installpath, case):
###############################################################################
    cime_model = case.get_value("MODEL")
    caseroot = case.get_value("CASEROOT")
    pio_version = case.get_value("PIO_VERSION")
    scorpio_src_root_dir = None
    if cime_model == "e3sm":
        srcroot = case.get_value("SRCROOT")
        scorpio_src_root_dir = os.path.join(srcroot, "externals")
        # Scorpio classic is derived from PIO1
        scorpio_classic_dir = "scorpio_classic"
        # Scorpio is derived from PIO2
        scorpio_dir = "scorpio"
        scorpio_classic_src_dir = os.path.join(scorpio_src_root_dir,
                                    scorpio_classic_dir)
        scorpio_src_dir = os.path.join(scorpio_src_root_dir, scorpio_dir)
        if (not os.path.isdir(scorpio_src_root_dir) or
            not os.path.isdir(scorpio_classic_src_dir) or
            not os.path.isdir(scorpio_src_dir)):
            scorpio_src_root_dir = None

    # If variable PIO_VERSION_MAJOR is defined in the environment then
    # we assume that PIO is installed on the system
    # and expect to find
    # PIO_LIBDIR, PIO_INCDIR, PIO_TYPENAME_VALID_VALUES
    # also defined in the environment.  In this case we
    # will use the installed pio and not build it here.
    installed_pio_version = os.environ.get("PIO_VERSION_MAJOR")
    logger.info("pio_version_major = {} pio_version = {}".format(installed_pio_version, pio_version))
    if installed_pio_version is not None and int(installed_pio_version) == pio_version:
        logger.info("Using installed PIO library")
        _set_pio_valid_values(case, os.environ.get("PIO_TYPENAME_VALID_VALUES"))
        return

    pio_model = "pio{}".format(pio_version)
    pio_dir = os.path.join(bldroot, pio_model)
    if not os.path.isdir(pio_dir):
        os.makedirs(pio_dir)
    casetools = case.get_value("CASETOOLS")
    cmake_opts = "\"-D GENF90_PATH=$CIMEROOT/src/externals/genf90 \""
    stdargs = get_standard_makefile_args(case, shared_lib=True)

    gmake_vars =  "CASEROOT={caseroot} MODEL={pio_model} COMP_NAME={pio_model} "\
                  "USER_CMAKE_OPTS={cmake_opts} "\
                  "PIO_LIBDIR={pio_dir} CASETOOLS={casetools} "\
                  "USER_CPPDEFS=-DTIMING"\
                  .format(caseroot=caseroot, pio_model=pio_model,
                          cmake_opts=cmake_opts, pio_dir=pio_dir,
                          casetools=casetools)

    if scorpio_src_root_dir is not None:
        gmake_vars += " IO_LIB_SRCROOT={scorpio_src_root_dir} "\
                      " IO_LIB_v1_SRCDIR={scorpio_classic_dir} "\
                      " IO_LIB_v2_SRCDIR={scorpio_dir} "\
                      .format(scorpio_src_root_dir=scorpio_src_root_dir,
                              scorpio_classic_dir=scorpio_classic_dir,
                              scorpio_dir=scorpio_dir)

    gmake_opts =  "{pio_dir}/Makefile -C {pio_dir} "\
                  " {gmake_vars} {stdargs} -f {casetools}/Makefile"\
                  .format(pio_dir=pio_dir, gmake_vars=gmake_vars,
                          casetools=casetools, stdargs=stdargs)

    gmake_cmd = case.get_value("GMAKE")

    # This runs the pio cmake command from the cime case Makefile
    cmd = "{} {}".format(gmake_cmd, gmake_opts)
    run_bld_cmd_ensure_logging(cmd, logger, from_dir=pio_dir)

    # This runs the pio make command from the cmake generated Makefile
    run_bld_cmd_ensure_logging("{} -j {}".format(gmake_cmd, case.get_value("GMAKE_J")), logger, from_dir=pio_dir)

    if pio_version == 1:
        installed_lib = os.path.join(installpath,"lib","libpio.a")
        installed_lib_time = 0
        if os.path.isfile(installed_lib):
            installed_lib_time = os.path.getmtime(installed_lib)
        newlib = os.path.join(pio_dir,"pio","libpio.a")
        newlib_time = os.path.getmtime(newlib)
        if newlib_time > installed_lib_time:
            logger.info("Installing pio version 1")
            safe_copy(newlib, installed_lib)
            for glob_to_copy in ("*.h", "*.mod"):
                for item in glob.glob(os.path.join(pio_dir,"pio",glob_to_copy)):
                    safe_copy(item, "{}/include".format(installpath))
        expect_string = "D_NETCDF;"
        pnetcdf_string = "D_PNETCDF"
        netcdf4_string = "D_NETCDF4"
    else:
        globs_to_copy = (os.path.join("src","clib","libpioc.*"),
                         os.path.join("src","flib","libpiof.*"),
                         os.path.join("src","clib","*.h"),
                         os.path.join("src","flib","*.mod"))
        for glob_to_copy in globs_to_copy:
            installed_file_time = 0
            for item in glob.glob(os.path.join(pio_dir,glob_to_copy)):
                if item.endswith(".a") or item.endswith(".so"):
                    installdir = "lib"
                else:
                    installdir = "include"
                installed_file = os.path.join(installpath,installdir,os.path.basename(item))
                item_time = os.path.getmtime(item)
                if os.path.isfile(installed_file):
                    installed_file_time = os.path.getmtime(installed_file)
                if item_time  > installed_file_time:
                    safe_copy(item, installed_file)
        expect_string = "NetCDF_C_LIBRARY-ADVANCED"
        pnetcdf_string = "PnetCDF_C_LIBRARY-ADVANCED"
        netcdf4_string = "NetCDF_C_HAS_PARALLEL:BOOL=TRUE"


    # make sure case pio_typename valid_values is set correctly
    expect_string_found = False
    pnetcdf_found = False
    netcdf4_parallel_found = False

    cache_file = open(os.path.join(pio_dir,"CMakeCache.txt"), "r")
    for line in cache_file:
        if re.search(expect_string, line):
            expect_string_found = True
        if re.search(pnetcdf_string, line):
            pnetcdf_found = True
        if re.search(netcdf4_string, line):
            netcdf4_parallel_found = True

    expect(expect_string_found, "CIME models require NETCDF in PIO build")
    valid_values = "netcdf"
    if pnetcdf_found:
        valid_values += ",pnetcdf"
    if netcdf4_parallel_found:
        valid_values += ",netcdf4p,netcdf4c"
    _set_pio_valid_values(case, valid_values)


def _set_pio_valid_values(case, valid_values):
    # nothing means use the general default
    valid_values += ",nothing"
    logger.warning("Updating valid_values for PIO_TYPENAME: {}".format(valid_values))
    env_run = case.get_env("run")
    env_run.set_valid_values("PIO_TYPENAME",valid_values)

    for comp in case.get_values("COMP_CLASSES"):
        comp_pio_typename = "{}_PIO_TYPENAME".format(comp)
        current_value = case.get_value(comp_pio_typename)
        if current_value not in valid_values:
            logger.warning("Resetting PIO_TYPENAME to netcdf for component {}".format(comp))
            env_run.set_value(comp_pio_typename,"netcdf")

def _main(argv, documentation):
    bldroot, installpath, caseroot = parse_command_line(argv, documentation)
    with Case(caseroot, read_only=False) as case:
        buildlib(bldroot, installpath, case)

if (__name__ == "__main__"):
    _main(sys.argv, __doc__)
